iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0

備忘錄模式能讓物件在不破壞封裝的情況下儲存與回復狀態。

生活範例

備忘錄模式的一個經典應用場景是歷史紀錄。這類紀錄在我們的日常生活中隨處可見,例如 Word、Photoshop 和 VS Code 等編輯軟體,都提供「上一步」和「下一步」功能,讓使用者在創作的過程中方便地復原或重做操作。這些歷史紀錄就像是備忘錄模式中的備忘錄,每個紀錄都代表著一個編輯狀態,你可以藉由切換紀錄來改變編輯狀態,從而改變編輯結果。

舉個例子

延續歷史紀錄的例子,我們來建立一個文字編輯軟體,並利用備忘錄模式來實現撤銷和重做功能。

EditorState 類別負責保存編輯器的狀態。它扮演備忘錄的角色,可以讓你保存目前的文字內容,並在需要的時候取出這些內容來恢復編輯器的內容。

class EditorState {
  constructor(private content: string) {}

  getContent(): string {
    return this.content;
  }
}

TextEditor 代表編輯器本身,負責更新文字內容和保存當前狀態。它也能根據提供的編輯器狀態來改變文字內容,你可以利用這項功能來撤銷或重做操作。

class TextEditor {
  private content: string;

  constructor(initialContent: string = "") {
    this.content = initialContent;
  }

  updateContent(newContent: string) {
    console.log(`TextEditor: Updating content to "${newContent}"`);
    this.content = newContent;
  }

  createState(): EditorState {
    return new EditorState(this.content);
  }

  restoreState(state: EditorState) {
    const restoredContent = state.getContent();
    console.log(
      `TextEditor: Restoring content "${restoredContent}" from state`
    );
    this.content = restoredContent;
  }
}

Application 是這個文字編輯軟體的主程式,負責管理編輯器和編輯器的歷史狀態。它可以讓使用者更新編輯器的內容,保存當前狀態,並撤銷和重做編輯內容。

class Application {
  private states: EditorState[];
  private currentIndex: number;
  private textEditor: TextEditor;

  constructor() {
    this.states = [];
    this.currentIndex = -1;
    this.textEditor = new TextEditor();
  }

  updateContent(newContent: string) {
    this.textEditor.updateContent(newContent);
    this.states = this.states.slice(0, this.currentIndex + 1);
    this.states.push(this.textEditor.createState());
    this.currentIndex++;
  }

  undo() {
    if (this.currentIndex > 0) {
      this.currentIndex--;
      this.textEditor.restoreState(this.states[this.currentIndex]);
    }
  }

  redo() {
    if (this.currentIndex < this.states.length - 1) {
      this.currentIndex++;
      this.textEditor.restoreState(this.states[this.currentIndex]);
    }
  }
}

我們初始化一個文字編輯軟體,更新幾次內容,然後重複撤銷和重做的操作,以展示狀態功能如何運作。

class ApplicationTestDrive {
  static main() {
    const app = new Application();

    app.updateContent("I love coding.");
    app.updateContent("I love listening to music.");
    app.undo();
    app.redo();
    app.updateContent("I love drinking coffee.");
    app.undo();
  }
}

ApplicationTestDrive.main();

執行結果:

TextEditor: Updating content to "I love coding."
TextEditor: Updating content to "I love listening to music."
TextEditor: Restoring content "I love coding." from state
TextEditor: Restoring content "I love listening to music." from state
TextEditor: Updating content to "I love drinking coffee."
TextEditor: Restoring content "I love listening to music." from state

定義

Memento Pattern

  • 管理員(Caretaker): 負責更新和管理發起人的狀態
  • 發起人(Originator): 擁有內部狀態的物件,負責建立備忘錄並使用它來回復狀態
  • 備忘錄(Memento): 儲存發起人的內部狀態的物件

備忘錄模式是一種儲存物件狀態的方法,經常被用來實現回復和版本控制功能。物件可以將內部狀態儲存成備忘錄物件,並透過備忘錄物件來回復到特定的狀態。這種方法可以維持資料的封閉性,避免在儲存狀態資訊的同時將物件的內部資訊暴露於外。此外,它也能避免物件和狀態管理機制綁在一起,讓程式的設計變得更加靈活。

總結

  • 將目標物件的當前狀態封裝成獨立的物件
  • 讓物件能在不破壞封裝的情況下儲存與回復狀態
  • 若備忘錄參考到外部物件或資源可能需要進一步的處理狀態
  • 在某些程式語言中,只有發起人能夠存取備忘錄的狀態,避免備忘錄的資料遭到修改

完整範例

https://github.com/chengen0612/design-patterns-typescript/blob/main/patterns/behavioral/memento.ts


上一篇
Day 25 - State 狀態
下一篇
Day 27 - Method Chaining 方法鏈
系列文
前端也想學設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言